/**
 * Pangolin IM Server
 * autor:   yiyefangzhou24
 * email:   yiyefangzhou24@qq.com
 * data:    21/10/1
 */

#include "function.h"



/**
 * [内部函数]
 * 系统初始化函数，主要用来同步mysql和临时数据库redis中数据
*/
void system_init()
{
   int num = mysql_count_row("user_relationship" , "where `remark` = 1");
   LOG("system_init : copy %d user relationship to redis" , num);
   if(num > 0){
      //初始化二维int数组存放好友关系表
      int **list = (int **)malloc(sizeof(int *) * num);
      for (int i = 0; i < num; i++)
      {
         list[i] = (int *)malloc(sizeof(int) * 2);
      }

      if(mysql_get_allfriendlist(list) > 0){
         for(int i = 0 ; i < num ; i++){
            LOG("system_init : add friendlist : %d - %d" , list[i][0] , list[i][1]);
            redis_add_relationship(list[i][0] , list[i][1]);
         }
      }

      for (int i = 0; i < num; i++)
         free(list[i]);
      free(list);
   }
   printf("\n-------------------------------------------------------\n\n\n");
}

int _get_friendlist(int uid , int * uid_list , char ** name_list , char ** headimg_list){
   return mysql_get_friendlist(uid , uid_list , name_list , headimg_list);
}

int _count_friendlistbyuid(int uid){
   char where[128] = { 0 };
   sprintf(where , "where (`remark` = 1) and (`user_id1`='%d' or `user_id2`='%d')" , uid , uid);
   int num = mysql_count_row("user_relationship" , where);
   return num;
}

/**
 * [内部函数]
 * 随机产生32位数字和小写字母组成的访问令牌
*/
void _random_string(char * buff, int n)
{
   char metachar[] = "abcdefghijklmnopqrstuvwxyz0123456789";
   srand(time(NULL));
   for (int i = 0; i < n; i++)
   {
      buff[i] = metachar[rand() % 36]; 
   }
   buff[n] = '\0';
}

/**
 * [内部函数]
 * 客户端出错（包括断开链接、超时等），删除redis中用户信息表
*/
bool _response_removeclient(int fd)
{
   return redis_del_userinfobyfd(fd);
}

/**
 * [内部函数]
 * 验证用户访问令牌是否正确，确定用户登陆状态
*/
bool _check_userkey(int uid , char * key){
   char db_key[40] = { 0 };
   if(redis_get_userkey(uid , db_key) == false){
      LOG("_check_userkey : error : %s" , redis_getlasterror());
      return false;
   }
   return strcmp(key , db_key) == 0 ? true : false;
}

/**
 * [内部函数]
 * 更新用户最后心跳时间
*/
bool _update_usertime(int uid){
   //获取当前时间戳
   time_t t = time(NULL);
   int timestamp = time(&t);
   return redis_update_usertime(uid , timestamp);
}

/**
 * [内部函数]
 * 将用户信息添加到redis数据库，格式为"key":"key","time":1633873249,"fd":12}
*/
bool _insert_userinfo(int uid , char * key , int fd){
   //如果有该用户数据先清空
   redis_del_userinfo(uid);
   //获取当前时间戳
   time_t t = time(NULL);
   int timestamp = time(&t);
   //将登陆信息和令牌缓存至redis用户表
   if(redis_save_userinfo(uid , key , fd , timestamp) == false){
      LOG("_insert_userkey : error : %s" , redis_getlasterror());
      return false;
   }
   return true;
}

/**
 * [内部函数]
 * 验证用户登陆信息
*/
bool _check_userlogin(int uid , char * passwd){
   //数据库验证用户信息
   if(mysql_check_login(uid , passwd) == false){
      LOG("_check_userlogin : error : %s" , mysql_getlasterror());
      return false;
   }
   
   return true;
}

/**
 * [内部函数]
 * 注册新用户
*/
bool _response_register(char * username , char * passwd , int * uid){
   //数据库创建用户
	int id = 0;
   if(mysql_add_user(username , passwd , &id) < 0){
      LOG("_response_register : error :%s" , mysql_getlasterror());
      *uid = 0;
      return false;
   }else{
      *uid = id;
      return true;
   }
}

/**
 * [内部函数]
 * 修改用户密码
*/
bool _response_repass(int uid , char * newpasswd){
   //数据库创建用户
   if(mysql_update_pass(uid , newpasswd) <= 0){
      LOG("_response_repass : error :%s" , mysql_getlasterror());
      return false;
   }else{
      return true;
   }
}

/**
 * [内部函数]
 * 修改用户信息
*/
bool _response_updateuserinfo(int uid , char * username , char * headimg){
   //数据库创建用户
   if(mysql_update_userinfo(uid , username , headimg) <= 0){
      LOG("_response_updateuserinfo : error :%s" , mysql_getlasterror());
      return false;
   }else{
      return true;
   }
}

/**
 * [内部函数]
 * 添加mysql的好友关系数据库
*/
bool _add_friendlist(int from , int to , int remark){
   return mysql_add_friendlist(from , to , remark) >= 0 ? true :false;
}

/**
 * [内部函数]
 * 删除好友关系包括mysql和redis数据库
*/
bool _del_friendlist(int from , int to){
   if(redis_del_relationship(from , to)){
      if(mysql_del_relationship(from , to)){
         return true;
      }else{
         return false;
      }
   }else{
      return false;
   }
}

/**
 * [内部函数]
 * 修改redis和mysql的好友关系数据库
*/
bool _update_friendlist(int from , int to , int remark){
   if(mysql_update_friendlist(from , to , remark) >= 0 ){
      if(remark == 1){  //确认好友消息需同步更新redis
         if(redis_add_relationship(to , from)){
            return true;
         }else{
            return false;
         }
      }else{
         return true;
      }

   }else{
      return false;
   }
}

/**
 * [内部函数]
 * 搜索好友
*/
bool _response_findfriend(int uid , char * username , char * headimg){
   return mysql_find_user(uid , username , headimg);
}

/**
 * [内部函数]
 * 验证是否为好友关系
 * 返回值：-1表示没有好友关系；0表示一方发起申请但另一方没有通过；1表示建立好友关系
*/
int _check_friend(int fromuid , int touid){
   
   if(redis_check_relationship(fromuid, touid)){      //检查redis数据库中关系表是否为好友
      return 1;
   }else{      //否则检查mysql数据库中关系表是否存在该关系表但一方未确认情况
      return mysql_get_friendlistremark(fromuid , touid);
   }
}

/**
 * [内部函数]
 * 验证用户是否在线
*/
bool _check_useronline(int uid){
   return redis_check_useronline(uid);
}

/**
 * [内部函数]
 * 字符串MD5加密
*/
void _string_md5encrypt(char * src , char *dst){
   MD5_CTX md5c;
   unsigned char decrypt[16]={0};
   char temp[8]={0};   
   MD5Init(&md5c);
   MD5Update(&md5c,(unsigned char *)src,strlen(src));
   MD5Final(&md5c,decrypt);
   for(int i=0;i<16;i++){
     sprintf(temp,"%02x",decrypt[i]);
     strcat(dst,temp);
   }
}

/**
 * [内部函数]
 * 用户退出
*/
bool _response_logout(int uid){
   //将redis中令牌信息删除
   if(redis_del_userinfo(uid) == false){
      LOG("_response_logout : error : %s" , redis_getlasterror());
      return false;
   }
   return true;
}

/**
 * [内部函数]
 * 将要发送的消息存入消息队列或者存入离线消息数据库
*/
bool _push_msg_queue(int fromuid , int touid , char * msg , int cmd){
   //要把包头的操作码一起存进去，否则读取消息线程不知道封装什么样的包头
   char * str_db = malloc(strlen(msg) + 20);
   memset(str_db , 0 , strlen(msg) + 20);
   sprintf(str_db , "%d:%s" , cmd , msg);

   //将消息内容进行base64编码
   LOG("_push_msg_queue ; msg : %s" , str_db);
   char msg_encode[MESSAGE_BUFF_MAX_LEN] = { 0 };
   base64_encode((void *)str_db , strlen(str_db) , msg_encode);
   LOG("_push_msg_queue ; enmsg : %s" , msg_encode);

   if(_check_useronline(touid)){
      return redis_push_msg_queue(msg_encode) > 0 ? true : false;
   }else{
      return mysql_push_msg_queue(fromuid , touid , msg_encode) > 0 ? true : false;
   }
   free(str_db);
}

/**
 * [内部函数]
 * 拉取用户离线消息队列
*/
int _pull_msg_queue(int uid , char msg[20][PACKAGE_BUFF_MAX_LEN]){
   char msg_encode[20][MESSAGE_BUFF_MAX_LEN] = {{ 0 }};
   int num = mysql_pull_msg(uid , msg_encode);
   for(int i = 0 ; i < num ; i++ ){
      LOG("_pull_msg_queue ; enmsg : %s" , msg_encode[i]);
      base64_decode((void *)msg_encode[i] , strlen(msg_encode[i]) , (void *)msg[i]);
      LOG("_pull_msg_queue ; msg : %s" , msg[i]);
   }
   return num;
}

// /**
//  * [内部函数]
//  * 获取用户离线消息数量
// */
// int _get_msg_queue_num(int uid){
//    return mysql_get_msg_num(uid);
// }

/**
 * [内部函数]
 * 文件MD5加密
*/
void _file_md5encrypt(char * src , char *dst){
   //https://cloud.tencent.com/developer/article/1815728
}


/**
 * [内部函数]
 * 验证用户输入，符合要求返回true，否则返回false
*/
bool _check_input(int type , void * src){
   if(type == IN_USERNAME){
      return strlen((char *)src) > 64 ? false : true;
   }else if(type == IN_PASSWD){
      return strlen((char *)src) > 64 ? false : true;
   }else if(type == IN_UID){
      return *(int *)src >= 10000 ? true : false;
   }else if(type == IN_OS){
      return (*(int *)src == 1 || *(int *)src == 2) ? true : false;
   }else if(type == IN_RESULT){
      return (*(int *)src == 1 || *(int *)src == 2) ? true : false;
   }else if(type == IN_KEY){
      return strlen((char *)src) == 32 ? true : false;
   }else if(type == IN_TYPE){
      return *(int *)src > 0 ? true : false;
   }else if(type == IN_CONTEXT){
      return strlen((char *)src) < PACKAGE_BUFF_MAX_LEN - PACKAGE_HEAD_LEN ? true : false;
   }else if(type == IN_PUBKEY){
      return strlen((char *)src) < PACKAGE_BUFF_MAX_LEN - PACKAGE_HEAD_LEN ? true : false;
   }else if(type == IN_HEADIMG){
      return strlen((char *)src) < 40 ? true : false;
   }else if(type == IN_MID){
      return strlen((char *)src) > 32 ? false : true;
   }else{
      return false;
   }
}